package com.sqlcheck.service.impl;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sqlcheck.entity.ScanTask;
import com.sqlcheck.model.SqlStatement;
import com.sqlcheck.service.CodeAnalysisService;
import com.sqlcheck.service.GitRepositoryService;
import com.sqlcheck.service.SqlContentExtractionService;
import com.sqlcheck.service.SqlExtractionResult;
import com.sqlcheck.service.TemporaryCredentialService;
import com.sqlcheck.enums.GitPlatform;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Git源码SQL内容提取服务实现
 */
@Slf4j
@Service("gitSqlExtractionService")
@RequiredArgsConstructor
public class GitSqlContentExtractionServiceImpl implements SqlContentExtractionService {

    private final ObjectMapper objectMapper;
    private final CodeAnalysisService codeAnalysisService;
    private final GitRepositoryService gitRepositoryService;
    private final TemporaryCredentialService temporaryCredentialService;

    @Override
    public SqlExtractionResult extractSqlContent(ScanTask task) {
        long startTime = System.currentTimeMillis();
        File localRepo = null;

        try {
            log.info("开始提取Git源码SQL: taskId={}", task.getId());

            // 1. 解析Git扫描配置
            GitScanConfig config = parseGitScanConfig(task.getConfigJson());

            // 2. 获取临时访问令牌
            String accessToken = getAccessToken(task.getId(), config);

            // 3. 克隆Git仓库
            localRepo = cloneGitRepository(config, accessToken, task.getId());

            // 4. 使用现有的代码分析服务提取SQL语句
            List<SqlStatement> rawStatements = codeAnalysisService.analyzeProject(localRepo.toPath());

            // 5. 为语句分配全局编号并规范化
            List<SqlStatement> numberedStatements = assignGlobalStatementNumbers(rawStatements);

            // 6. 构建提取元数据
            long extractionTime = System.currentTimeMillis() - startTime;
            SqlExtractionResult.ExtractionMetadata metadata = buildGitExtractionMetadata(
                    config, numberedStatements, extractionTime);

            log.info("Git源码SQL提取完成: taskId={}, 提取到{}条SQL语句, 耗时{}ms",
                    task.getId(), numberedStatements.size(), extractionTime);

            return SqlExtractionResult.success(numberedStatements, metadata);

        } catch (Exception e) {
            log.error("Git源码SQL提取失败: taskId={}", task.getId(), e);
            return SqlExtractionResult.failure("Git源码SQL提取失败: " + e.getMessage());
        } finally {
            // 清理Git资源
            cleanupGitResources(localRepo);
        }
    }

    /**
     * 解析Git扫描配置
     */
    private GitScanConfig parseGitScanConfig(String configJson) {
        try {
            if (configJson == null || configJson.trim().isEmpty()) {
                throw new IllegalArgumentException("Git扫描配置为空");
            }

            JsonNode jsonNode = objectMapper.readTree(configJson);
            GitScanConfig config = new GitScanConfig();

            // Git仓库信息
            config.setRepositoryUrl(getJsonString(jsonNode, "repositoryUrl"));
            config.setBranch(getJsonString(jsonNode, "branchName", "master"));
            config.setGitPlatform(getJsonString(jsonNode, "gitPlatform"));

            // 数据库类型配置
            config.setSourceDbType(getJsonString(jsonNode, "sourceDbType", "MYSQL"));
            config.setTargetDbType(getJsonString(jsonNode, "targetDbType", "GOLDENDB"));

            // 扫描过滤配置
            config.setIncludePatterns(parseStringList(jsonNode, "includePatterns"));
            config.setExcludePatterns(parseStringList(jsonNode, "excludePatterns"));

            if (config.getRepositoryUrl() == null || config.getRepositoryUrl().trim().isEmpty()) {
                throw new IllegalArgumentException("Git仓库地址不能为空");
            }

            return config;

        } catch (Exception e) {
            throw new RuntimeException("解析Git扫描配置失败: " + e.getMessage(), e);
        }
    }

    /**
     * 获取访问令牌
     */
    private String getAccessToken(Long taskId, GitScanConfig config) {
        try {
            String platformStr = config.getGitPlatform();
            if (platformStr == null) {
                platformStr = detectGitPlatform(config.getRepositoryUrl());
            }

            GitPlatform platform = GitPlatform.valueOf(platformStr.toUpperCase());
            String accessToken = temporaryCredentialService.getGitToken(taskId, platform);
            if (accessToken == null || accessToken.trim().isEmpty()) {
                throw new RuntimeException("获取Git访问令牌失败或令牌已过期");
            }

            return accessToken;

        } catch (Exception e) {
            log.error("获取Git访问令牌失败: taskId={}", taskId, e);
            throw new RuntimeException("获取Git访问令牌失败: " + e.getMessage());
        }
    }

    /**
     * 自动检测Git平台
     */
    private String detectGitPlatform(String repositoryUrl) {
        String lowerUrl = repositoryUrl.toLowerCase();
        if (lowerUrl.contains("github.com"))
            return "GITHUB";
        if (lowerUrl.contains("gitee.com"))
            return "GITEE";
        if (lowerUrl.contains("gitlab.com") || lowerUrl.contains("gitlab."))
            return "GITLAB";
        return "GITHUB"; // 默认
    }

    /**
     * 克隆Git仓库
     */
    private File cloneGitRepository(GitScanConfig config, String accessToken, Long taskId) {
        try {
            log.info("开始克隆Git仓库: url={}, branch={}", config.getRepositoryUrl(), config.getBranch());

            // 构建本地目录路径
            String localDirName = "task-" + taskId;

            // 使用GitRepositoryService克隆仓库
            File localRepo = gitRepositoryService.cloneRepository(
                    config.getRepositoryUrl(),
                    config.getBranch(),
                    accessToken,
                    localDirName);

            if (localRepo == null || !localRepo.exists()) {
                throw new RuntimeException("Git仓库克隆失败");
            }

            log.info("Git仓库克隆完成: localPath={}", localRepo.getAbsolutePath());
            return localRepo;

        } catch (Exception e) {
            log.error("克隆Git仓库失败: url={}", config.getRepositoryUrl(), e);
            throw new RuntimeException("Git仓库克隆失败: " + e.getMessage());
        }
    }

    /**
     * 为SQL语句分配全局编号
     */
    private List<SqlStatement> assignGlobalStatementNumbers(List<SqlStatement> rawStatements) {
        log.info("为{}条SQL语句分配全局编号", rawStatements.size());

        for (int i = 0; i < rawStatements.size(); i++) {
            SqlStatement statement = rawStatements.get(i);
            statement.setStatementNumber(i + 1);

            if (log.isDebugEnabled()) {
                log.debug("语句#{}: {} | 来源: {} | 类型: {}",
                        statement.getStatementNumber(),
                        truncateContent(statement.getContent(), 60),
                        statement.getSourceFile(),
                        statement.getSourceType());
            }
        }

        return rawStatements;
    }

    /**
     * 构建Git提取元数据
     */
    private SqlExtractionResult.ExtractionMetadata buildGitExtractionMetadata(
            GitScanConfig config, List<SqlStatement> statements, long extractionTime) {

        // 按来源类型统计语句数量
        Map<String, Integer> sourceTypeCounts = statements.stream()
                .collect(Collectors.groupingBy(
                        stmt -> getSourceTypeDisplayName(stmt.getSourceType()),
                        Collectors.collectingAndThen(Collectors.counting(), Math::toIntExact)));

        // 构建额外信息
        Map<String, Object> additionalInfo = new HashMap<>();
        additionalInfo.put("gitPlatform", config.getGitPlatform());
        additionalInfo.put("branch", config.getBranch());
        additionalInfo.put("sourceDbType", config.getSourceDbType());
        additionalInfo.put("targetDbType", config.getTargetDbType());

        // 按SQL类型统计
        Map<String, Long> sqlTypeStats = statements.stream()
                .collect(Collectors.groupingBy(
                        stmt -> stmt.getSqlType().name(),
                        Collectors.counting()));
        additionalInfo.put("sqlTypeBreakdown", sqlTypeStats);

        // 按源文件统计
        Map<String, Long> sourceFileStats = statements.stream()
                .collect(Collectors.groupingBy(
                        SqlStatement::getSourceFile,
                        Collectors.counting()));
        additionalInfo.put("sourceFileCount", sourceFileStats.size());
        additionalInfo.put("maxStatementsPerFile",
                sourceFileStats.values().stream().mapToLong(Long::longValue).max().orElse(0));

        return SqlExtractionResult.ExtractionMetadata.builder()
                .extractionType("GIT_SOURCE_SCAN")
                .sourceLocation(config.getRepositoryUrl())
                .totalStatements(statements.size())
                .sourceTypeCounts(sourceTypeCounts)
                .extractionTimeMs(extractionTime)
                .additionalInfo(additionalInfo)
                .build();
    }

    private String getSourceTypeDisplayName(SqlStatement.SourceType sourceType) {
        switch (sourceType) {
            case MYBATIS_XML:
                return "MyBatis XML标签";
            case MYBATIS_ANNOTATION:
                return "MyBatis注解";
            case JPA_ANNOTATION:
                return "JPA注解";
            case DDL_SCRIPT:
                return "数据库DDL";
            case MANUAL_INPUT:
                return "手动输入";
            default:
                return sourceType.name();
        }
    }

    /**
     * 清理Git资源
     */
    private void cleanupGitResources(File localRepo) {
        if (localRepo != null && localRepo.exists()) {
            try {
                // 递归删除本地仓库目录
                deleteDirectory(localRepo);
                log.info("Git仓库清理完成: {}", localRepo.getAbsolutePath());
            } catch (Exception e) {
                log.warn("Git仓库清理失败: {}", localRepo.getAbsolutePath(), e);
            }
        }
    }

    /**
     * 递归删除目录
     */
    private void deleteDirectory(File directory) {
        if (directory.isDirectory()) {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    deleteDirectory(file);
                }
            }
        }
        directory.delete();
    }

    /**
     * 截断内容用于日志显示
     */
    private String truncateContent(String content, int maxLength) {
        if (content == null)
            return null;
        if (content.length() <= maxLength)
            return content;
        return content.substring(0, maxLength) + "...";
    }

    // === 辅助方法 ===

    private String getJsonString(JsonNode node, String fieldName) {
        return getJsonString(node, fieldName, null);
    }

    private String getJsonString(JsonNode node, String fieldName, String defaultValue) {
        if (node.has(fieldName) && !node.get(fieldName).isNull()) {
            return node.get(fieldName).asText();
        }
        return defaultValue;
    }

    private List<String> parseStringList(JsonNode node, String fieldName) {
        if (!node.has(fieldName) || node.get(fieldName).isNull()) {
            return new ArrayList<>();
        }

        JsonNode arrayNode = node.get(fieldName);
        List<String> result = new ArrayList<>();

        if (arrayNode.isArray()) {
            arrayNode.forEach(item -> {
                if (!item.isNull()) {
                    result.add(item.asText());
                }
            });
        } else {
            // 如果是字符串，按逗号分割
            String str = arrayNode.asText();
            if (str != null && !str.trim().isEmpty()) {
                Arrays.stream(str.split(","))
                        .map(String::trim)
                        .filter(s -> !s.isEmpty())
                        .forEach(result::add);
            }
        }

        return result;
    }

    /**
     * Git扫描配置
     */
    private static class GitScanConfig {
        private String repositoryUrl;
        private String branch;
        private String gitPlatform;
        private String sourceDbType;
        private String targetDbType;
        private List<String> includePatterns;
        private List<String> excludePatterns;

        // Getters and Setters
        public String getRepositoryUrl() {
            return repositoryUrl;
        }

        public void setRepositoryUrl(String repositoryUrl) {
            this.repositoryUrl = repositoryUrl;
        }

        public String getBranch() {
            return branch;
        }

        public void setBranch(String branch) {
            this.branch = branch;
        }

        public String getGitPlatform() {
            return gitPlatform;
        }

        public void setGitPlatform(String gitPlatform) {
            this.gitPlatform = gitPlatform;
        }

        public String getSourceDbType() {
            return sourceDbType;
        }

        public void setSourceDbType(String sourceDbType) {
            this.sourceDbType = sourceDbType;
        }

        public String getTargetDbType() {
            return targetDbType;
        }

        public void setTargetDbType(String targetDbType) {
            this.targetDbType = targetDbType;
        }

        public List<String> getIncludePatterns() {
            return includePatterns;
        }

        public void setIncludePatterns(List<String> includePatterns) {
            this.includePatterns = includePatterns;
        }

        public List<String> getExcludePatterns() {
            return excludePatterns;
        }

        public void setExcludePatterns(List<String> excludePatterns) {
            this.excludePatterns = excludePatterns;
        }
    }
}